#include "gamearea.h"


#include <QTimerEvent>
#include <QMessageBox>
#include <QKeyEvent>
#include <QTime>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>

#define RECT_COLUMES    15   //游戏区15个小格子宽
#define RECT_ROWS        20   //游戏区20个小格子高
#define RECT_WIDTH        40   //每个小格子40个像素宽
#define RECT_HEIGHT        40   //每个小格子40个像素高

//默认出生点 x方向
#define DEFAULT_BORN_POS_X    (RECT_COLUMES / 2 - 1)



GameArea::GameArea(QWidget *parent) : QWidget(parent) //构造函数
{
    mScore = 0;
    mLevel = 1;
    item_swap=true;
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("Teris.db");

    if (!db.open()) {
        qDebug() << "Error: connection with database failed";
    } else {
        qDebug() << "Database: connection ok";
    }

    setMinimumSize(RECT_COLUMES*RECT_WIDTH, RECT_ROWS*RECT_HEIGHT);

}


void GameArea::NewGame()  //开始游戏
{
    mFixItems.mPoints.clear();   //清除已落下固定的格子的座标集


    //mCurItem 和 mNextItem 使用不同随机因子，避免初始一样
    mCurItem.InitNew(QTime::currentTime().msec()); //产生当前方块的座标集

    mCurItem.MoveTo(DEFAULT_BORN_POS_X, 1);//移动当前方块到初始位置

    mNextItem.InitNew(QTime::currentTime().second());//产生下一个方块的座标集

    emit sigUpdateNextItem(mNextItem.mType,mNextItem.mShape);//传送下一个方块的消息

    mScore = 0; //初始化
    mLevel = 1; //初始化
    emit sigUpdateScore(mScore);    //更新MainWindow界面得分
    emit sigUpdateLevel(mLevel);    //更新MainWindow界面等级
    mTimerID = startTimer( GetLevelTime(mLevel) ); //创建定时器
    playTime.start(); //游戏开始计时

}



void GameArea::paintEvent(QPaintEvent *)//绘图事件，当窗体发生变更时，自动执行
{
    //绘制左侧游戏区域
    DrawBKRects();      //绘制游戏区被景
    DrawFixedRects();   //绘制固定方块
    DrawCurItem();      //绘制当前方块

    update();           //更新窗体
}


void GameArea::DrawBKRects()
{
    QPainter painter(this); //定义一个本窗体的画笔
    painter.setBrush(QColor("#696969")); //定义填冲的颜色或格式
    painter.setPen(Qt::NoPen); //定义边框的颜色和类型
    for(int i=0;i<RECT_COLUMES;i++) //遍历游戏区域
    {
        for(int j=0;j<RECT_ROWS;j++)
        {
            //第0列和最后一列和第0行和最后一行
            if(i==0||i==RECT_COLUMES-1||j==0||j==RECT_ROWS-1)
                //drawRect(左上角x座标，左上角y座标，方格宽，方格高）
                painter.drawRect(i*RECT_HEIGHT,j*RECT_WIDTH,RECT_WIDTH,RECT_HEIGHT);
        }
    }

}

void GameArea::DrawFixedRects()  //画下落后已固定不动的方块
{
    QPainter painter(this); //定义一个画笔
    painter.setBrush(QColor("#D3D3D3"));//定义填冲的颜色或格式
    painter.setPen(QPen(QColor(Qt::black),1)); //定义边框的颜色和类型
    mFixItems.Draw(painter,0,0,RECT_WIDTH,RECT_HEIGHT);


}

void GameArea::DrawCurItem()  //画当前下落中的方块
{
    QPainter painter(this);
    painter.setBrush(QColor("#FFDEAD"));
    painter.setPen(QPen(QColor(Qt::black),1));

    mCurItem.Draw(painter,0,0,RECT_WIDTH,RECT_HEIGHT);
}



bool GameArea::HitSide() //判断当前下落方块是否超左右边界
{
    for (int i = 0; i<mCurItem.mPoints.size(); i++) //遍历当前下落方块的坐标
    {
        QPoint pt = mCurItem.mPoints[i];
        if (pt.x() <= 0 || pt.x() >= RECT_COLUMES - 1)//如果X坐标小于等于0或大于等于RECT_COLUMES - 1，则撞到边界了
        {
            return true; //返回true
        }
    }
    return false;
}


bool GameArea::HitTop() //判断当前下落方块是否超上边界
{
    for (int i=0;i<mCurItem.mPoints.size();i++) //遍历当前下落方块的坐标
    {
        QPoint pt=mCurItem.mPoints[i];
        if(pt.y()<=1) //如果y座标小于等于1， 则表示撞上边界了
            return true;
    }
    return false;
}


bool GameArea::HitBottom() //判断当前下落方块是否撞下边界
{
    for (int i=0;i<mCurItem.mPoints.size();i++)
    {
        QPoint pt=mCurItem.mPoints[i];
        if(pt.y()>=RECT_ROWS-1)  //如果y座标大于等于RECT_ROWS-1，则表示撞上下边界了
            return true;
        if(mFixItems.mPoints.contains(pt))// 如果固定不动的方块的座标包含当前下落方块的座标，则表示下到底了
            return true;
    }
    return false;
}

void GameArea::KeyPressed(int key) //按键按压事件
{

    int x = 0;
    int y = 0;
    switch(key)
    {
    case Qt::Key_Left: //左移
    {
        x = -1;
        break;
    }
    case Qt::Key_Up://改变形状态
    {
        mCurItem.ChangeShape();
        if(HitSide() || HitBottom())
        {
            mCurItem.ChangeShape(-1);
        }
        return;
    }
    case Qt::Key_Right: //右移
    {
        x = 1;
        break;
    }
    case Qt::Key_Down://下移
    {
        y = 1;
        break;

    }
    case Qt::Key_Space:
    {
        //空格键直接移到底部
        while(1)
        {
            mCurItem.Move(0,1); //下移一行
            if(HitBottom())  //如果撞到下边界或固定区域方块
            {
                mCurItem.Move(0,-1); //上移一行
                break;  //退出循环
            }
        }
        return;//返回
    }
    case Qt::Key_Shift:
    {
        if(item_swap){
        mCurItem = mNextItem; //产生新的当前方块类形，直接用Vector 值赋值Vector 变量
        mCurItem.MoveTo(DEFAULT_BORN_POS_X, 1); //移到中间位置
        mNextItem.InitNew(); //产生新的下一个方块类形
        item_swap=false;
        }
    }


    }
    mCurItem.Move(x,y); //移动x列，y行

    if (HitSide() || HitBottom()) //如果撞到边界，下边界或固定区域方块
    {
        mCurItem.Move(-x,-y); //退回x列，y行
    }



}

void GameArea::AddToFixedRects() //把当前方块座标加入到固定方块
{
    mFixItems.AddPoints(mCurItem.mPoints);
    item_swap=true;
}


void GameArea::DeleteFullRows() //删除完整的行
{
    int nRowsDeleted = 0; //记录消的行数
    for (int i = 1; i<RECT_ROWS-1; i++) //遍历游戏区座标
    {
        int nCount = 0;
        for (int j = 1; j<RECT_COLUMES-1; j++)
        {
            if (mFixItems.mPoints.contains(QPoint(j,i))) //判定每行有多少位置有方块
            {
                nCount++; // 每行方块的个数
            }
        }
        if (nCount == RECT_COLUMES-2)// 如果每行的个数等于Rect_Columes-2, 则代表是满的
        {
            mFixItems.DeleteRow(i);
            mFixItems.MoveDown(i,1);    //消除行之上的内容下移一个单位
            nRowsDeleted++;
        }
    }

    //一次元素落下，最多可能消4行
    //一次消除的越多，得分越多
    if (nRowsDeleted == 1)
    {
        mScore += 100;
    }
    else if (nRowsDeleted == 2)
    {
        mScore += 300;
    }
    else if (nRowsDeleted == 3)
    {
        mScore += 500;
    }
    else if (nRowsDeleted == 4)
    {
        mScore += 700;
    }
    emit sigUpdateScore(mScore);    //更新MainWindow界面得分

    //粗略使用每1000分一关
    if(mScore >= 1000 * mLevel)
    {
        mLevel++;

        //随关卡增加下落速度，即把定时器加快
        killTimer(mTimerID);
        mTimerID = startTimer( GetLevelTime(mLevel) );

        emit sigUpdateLevel(mLevel);    //更新MainWindow界面关卡
    }
}



int GameArea::GetLevelTime(int level)
{
    //第1关=1000ms,第2关=900ms 越来越快 第8关=300ms
    //关卡>8后，速度不再增加，保持200ms

    if (level > 0 && level<=8)
    {
        return (11 - level) * 100;
    }
    else
    {
        return 200;
    }
}

void GameArea::timerEvent(QTimerEvent* e) //定时事件
{
    if (e->timerId() == mTimerID)
    {
        mCurItem.Move(0,1);//向下移一格
        if (HitBottom()) //如果撞到下边界或固定方块区，
        {
            mCurItem.Move(0,-1);//向上移一格
            AddToFixedRects(); //添加当前下落方块到固定方块
            DeleteFullRows();  //判定是否有满格行，若有，则删行

            if (HitTop()) //如果撞到上边界
            {
                QSqlQuery query;
                if (query.exec("SELECT MAX(score) FROM Score")) {
                    while (query.next()) {
                        maxScore = query.value(0).toInt();
                    }
                }
                killTimer(mTimerID); //结束定时器，提醒Game over.
                QMessageBox::information(nullptr, "游戏结束", QString("游戏结束！\n历史最高得分:%1\n你的得分：%2").arg(maxScore).arg(mScore));

                query.exec("CREATE TABLE Score (score int)");

                // 插入数据
                query.prepare("INSERT INTO Score (score) VALUES (:score)");
                query.bindValue(":score", mScore);
                if (!query.exec()) {
                    qDebug() << "Insert error:" << query.lastError();
                }

                return;
            }

            mCurItem = mNextItem; //产生新的当前方块类形，直接用Vector 值赋值Vector 变量
            mCurItem.MoveTo(DEFAULT_BORN_POS_X, 1); //移到中间位置

            mNextItem.InitNew(); //产生新的下一个方块类形
            emit sigUpdateNextItem(mNextItem.mType,mNextItem.mShape); //发送下一个方块类型信号
        }

        emit sigUdateTimeEslape(playTime.elapsed()); //发送游戏时间信号
    }
}


void GameArea::PauseGame(bool gamestatus) //暂停游戏
{
    if(gamestatus)
        killTimer(mTimerID); //停止定时器
    else
    {
        mTimerID=startTimer(GetLevelTime(mLevel)); //开始定时器
        playTime.restart(); //重新计时

    }
}

void GameArea::StopGame() //停止游戏
{

    mCurItem.mPoints.clear(); //Vector 的clear 函数，清除座标
    mNextItem.mPoints.clear();
    mFixItems.mPoints.clear();
    killTimer(mTimerID);// 停止定时器

}
